This page last changed on Oct 20, 2006 by cholmes.

Extending GeoServer's configuration IO has four steps:

  1. Create a Bean or Attribute to store your new information
  2. Load your information from XML
  3. Use you information to set up GeoServer
  4. Write your information to XML

Although in practice many of these processes are completed concurently and closely depend upon each other, we are going to pretend we live in a perfect linear world.

Data Transfer Objects

The DTO objects closely resemble the structure of the XML files. The DTO objects should contain data in a logical grouping, independant of the application and user interface. So if it belongs with other like data, put it there. For example above we would store serviceLevel as an enumerated type in the WFSDTO object. But if we also wanted to store serviceLevels for the WMS service this information would be stored in the ServiceDTO Object, with the enumeration of types residing in the WFSDTO and WMSDTO Objects respectively.

Data Transfer Object Extension

Make sure all of the following tasks have been completed (example from WFSDTO.java):

Update default construtor

public WFSDTO(){
    serviceLevel = WFSDTO.BASIC; // <-- new
}

Update parametarized constructor used to clone

public WFSDTO(WFSDTO other) {
    if (other == null) {
        throw new NullPointerException("Data Transfer Object required");
    }

    service = (ServiceDTO) new ServiceDTO(other.getService());
    gmlPrefixing = other.isGmlPrefixing();
    serviceLevel = WFSDTO.getServiceLevel(); // <-- new
}

Update equals method

public boolean equals(Object other) {
    if ((other == null) || !(other instanceof WFSDTO)) {
        return false;
    }

    WFSDTO dto = (WFSDTO) other;

    return (serviceLevel == dto.getServiceLevel() && // <-- new
            service == null) ? (dto.getService() == null) 
                             : service.equals(dto.getService()));
}

Generate getter and setter methods

public int getServiceLevel(){
    return serviceLevel;
}

public void setServiceLevel(int serviceLevel){
    this.serviceLevel = serviceLevel;
}

Document the changes in the source code

/** ServiceLevel bit used to indicate Basic support */
public static final int SERVICE_BASIC = 1;

/** ServiceLevel bit used to indicate Transaction Insert support */
public static final int SERVICE_INSERT = 2;

/** ServiceLevel bit used to indicate Transaction Update support */
public static final int SERVICE_UPDATE = 4;

/** ServiceLevel bit used to indicate Transaction Delete support */
public static final int SERVICE_DELETE = 8;

/** ServiceLevel bit used to indicate Locking support */
public static final int SERVICE_LOCKING = 16;

/** ServiceLevel mask equivilent to basic WFS conformance */
public static final int BASIC = 1;

/** ServiceLevel mask for transactional WFS conformance. */
public static final int TRANSACTIONAL = SERVICE_BASIC | SERVICE_INSERT
        | SERVICE_UPDATE | SERVICE_DELETE;

/** ServiceLevel mask equivilent to complete WFS conformance */
public static final int COMPLETE = TRANSACTIONAL | SERVICE_LOCKING;

XMLConfigReader

Extending the XML Reader is relatively easy to complete, but equally easy to break the application. As a general warning if you are not happy playing with DOM trees you should brush up on your XML skills before entering this stage of developement.

The file XMLConfigReader may extended at several locations:

  • XMLConfigReader( File rootDir ) - calls load method on creation
  • load() - extend to add support for additional configuration files
  • loadService(Element) - extend for parsing common to both WMS and WFS
  • loadWMS(Element) - extend for parsing Web Map Server
  • loadWFS(Element) - extend for parsing Web Feature Server
  • loadGlobal(Elemenet) - extend for parsing GeoServerDTO
  • load

For the DOM efficiados out there all you need to do is add the code to read the Element at the correct point in the file which you intend to read from. Often this is easier said than done, but once completed place the data into the DTO object.

The best advice I have for you is to be VERY careful of your element pointer withing the DOM tree. If you inadvertantly affect this pointer, all other parsing one the remaining interior branches of that tree will not be parsed correctly, mostlikely breaking atleast part of the application.

How to be VERY careful? Try one of the following:

  • make a private method an pass the ref in - for an example check out
  • make a copy of the reference before playing with it

Just like the previous step, I have included a short list to help ensure you have completed everything (example XMLConfigReader.java).

Ensure you have not affected the Element references around you new addition (XMLConfigReader.processSchema() )

// SAFER
elem = ReaderUtils.getChildElement(elem, "xs:sequence");
NodeList nl = elem.getElementsByTagName("xs:element");
for (int i = 0; i < nl.getLength(); i++) {
    // one element now
    Element element = (Element) nl.item(i);

Test the application to ensure it is not broken (ie. run cite tests)

Document the changes in the source code

elem = ReaderUtils.getChildElement(elem, "xs:sequence");
NodeList nl = elem.getElementsByTagName("xs:element");
for (int i = 0; i < nl.getLength(); i++) {
    // Create a new reference to walk through all the child nodes
    Element element = (Element) nl.item(i);

Ensure you have included informative error messages where appropriate.

// Bad 
catch(ConfigurationException e){
   throw new ConfigurationException("io error");
}
// Good
catch(ConfigurationException e){
   throw new ConfigurationException("Error occured in "+schemaFile+"\n"+e.getMessage(),e);
}

Optimization

You may optimize this process for speed (rather than saftey):

// UNSAFE - see the real code example above for comparison
elem = ReaderUtils.getChildElement(elem, "xs:sequence");
NodeList nl = elem.getElementsByTagName("xs:element");
for (int i = 0; i &#60; nl.getLength(); i++) {
    // one element now
    elem = (Element) nl.item(i);

And once again be VERY careful, if you loose the readers place in the DOM tree. The errors will not be thrown from your code, but from else where in the reading process.

XMLConfigWriter

This portion is the reverse of the XML Reader. You need to add the write methods for your new data. Try to use the element write methods provided. Also you should be careful to match tags ... . Remember to test this code as well, because this code will break the application as badly as the reader next time you run it.

  • Test the application to ensure it is not broken (ie. run cite tests)
  • Document the changes in the source code
    // write the service level if not basic (our default) 
    if ((wfs.getTitle() != null) && (wfs.getTitle() != WFSDTO.BASIC)) {
        cw.textTag("serviceLevel", wfs.getServiceLevel()+"");
    }
  • Ensure you have included informative error messages where appropriate.
    // Bad 
    catch(ConfigurationException e){
       throw new ConfigurationException("io error");
    }
    // Good
    catch(ConfigurationException e){
       throw new ConfigurationException("Error occured in WFS service configuration output.\n"+e.getMessage(),e);
    }
Document generated by Confluence on Jan 16, 2008 23:26